package com.yidian.task;

import com.yidian.dao.SeckillGoodsMapper;
import com.yidian.pojo.seckill.SeckillGoods;
import com.yidian.util.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import tk.mybatis.mapper.entity.Example;

import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * @author whb
 * @create 2021-09-27 10:36
 * @desciption 秒杀商品定时压入Redis缓存逻辑类
 */
@Component
public class SeckillGoodsPushTask {
    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Scheduled(cron = "0/30 * * * * ?")
    public void queryGoodsPushToRedis() {
        List<Date> dateMeuns = DateUtil.getDateMenus();//获取时间段集合
        for (Date startTime : dateMeuns) {
            String redisKey = DateUtil.date2Str(startTime);//作为秒杀商品存入Redis的key
            //设置符合秒杀商品的查询条件
            Example example = new Example(SeckillGoods.class);
            Example.Criteria criteria = example.createCriteria();
            criteria.andEqualTo("status", 1);//商品审核通过
            criteria.andGreaterThan("stockCount", 0);//库存大于0
            criteria.andGreaterThanOrEqualTo("startTime", startTime);//商品的开始时间大于等于活动起始时间
            criteria.andLessThan("endTime", DateUtil.addDateHour(startTime, 2));//商品的结束时间小于活动结束时间
            //排除之前redis缓存中已经加载的商品数据
            Set keys = redisTemplate.boundHashOps("SeckillGoods_" + redisKey).keys();
            if (keys != null && keys.size() > 0) {
                criteria.andNotIn("id", keys);
            }
            List<SeckillGoods> seckillGoodList = seckillGoodsMapper.selectByExample(example);

            //System.out.println(seckillGoodList.size());

            for (SeckillGoods seckillGood : seckillGoodList) {
                redisTemplate.boundHashOps("SeckillGoods_" + redisKey).put(seckillGood.getId(), seckillGood);//将符合秒杀的商品存入redis缓存中

                //剩余库存个数  seckillGood.getStockCount()   =   5
                //              创建独立队列:存储商品剩余库存
                //              SeckillGoodsList_110:[110,110,110,110,110];其中110是商品id
                Long[] ids = pushIds(seckillGood.getId(), seckillGood.getStockCount());
                /**
                 * 创建库存数量队列，redis中List本身就是队列，是单线程，所以可以解决多线程中的线程安全问题。
                 * 能够解决在多线程下抢单，出现的超卖现象
                 */
                redisTemplate.boundListOps("SeckillGoodsCountList_"+seckillGood.getId()).leftPushAll(ids);
                redisTemplate.boundHashOps("SeckillGoodsCount").increment(seckillGood.getId(),seckillGood.getStockCount());//创建库存自增队列，避免直接在多线程中异步操作redis缓存而出现的数据错乱
            }
        }
    }

    /**
     * 组装商品id，将商品id组装成数组
     *
     * @param id       商品id
     * @param stockNum 库存数量
     * @return
     */
    public Long[] pushIds(Long id, Integer stockNum) {
        Long[] ids = new Long[stockNum];
        for (int i = 0; i < stockNum; i++) {
            ids[i] = id;
        }
        return ids;
    }
}
